package com.lanou3g.ptp.task.service.impl;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.dubbo.config.annotation.Service;
import com.alibaba.fastjson.JSON;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.lanou3g.ptp.achievement.service.DiaryService;
import com.lanou3g.ptp.commons.DayOfWeekDateHandler;
import com.lanou3g.ptp.commons.DefaultDateHandler;
import com.lanou3g.ptp.commons.TaskDateHandler;
import com.lanou3g.ptp.commons.WorkdayDateHandler;
import com.lanou3g.ptp.entity.*;
import com.lanou3g.ptp.exception.PtpException;
import com.lanou3g.ptp.signInDate.mapper.SignInDateMapper;
import com.lanou3g.ptp.signInDate.service.SignInDateService;
import com.lanou3g.ptp.sso.service.SsoService;
import com.lanou3g.ptp.task.mapper.TaskMapper;
import com.lanou3g.ptp.task.service.TaskService;
import com.lanou3g.ptp.taskrole.mapper.TaskRoleMapper;
import com.lanou3g.ptp.taskrole.service.TaskRoleService;
import com.lanou3g.ptp.taskstudent.mapper.TaskStudentMapper;
import com.lanou3g.ptp.taskstudent.service.TaskStudentService;
import com.lanou3g.ptp.taskwarningplan.mapper.TaskWarningPlanMapper;
import com.lanou3g.ptp.teachlink.mapper.TaskGradeItemMapper;
import com.lanou3g.ptp.utils.DateUtils;
import com.lanou3g.ptp.vo.*;
import com.lanou3g.ptp.warning.service.WarningService;
import lombok.extern.slf4j.Slf4j;
import org.apache.activemq.command.ActiveMQTopic;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@SuppressWarnings("SpringJavaAutowiredMembersInspection")
@Service(interfaceClass = TaskService.class, timeout = 10000)
@Slf4j
public class TaskServiceImpl implements TaskService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Value("${REDIS_TASK_KEY_PREFIX}")
    private String REDIS_TASK_KEY_PREFIX;

    @Value("${REDIS_TASK_KEY_EXPIRE}")
    private Long REDIS_TASK_KEY_EXPIRE;
    @Value("TASK-INSERT")
    private String ACTIVEMQ_TOPIC_TASK_KEY;
    @Autowired
    private TaskRoleService taskRoleService;
    @Autowired
    private TaskStudentService taskStudentService;
    @Autowired
    private SignInDateService signInDateService;
    @Autowired
    private TaskMapper taskMapper;

    @Autowired
    private SignInDateMapper signInDateMapper;

    @Autowired
    private TaskStudentMapper taskStudentMapper;

    @Autowired
    private TaskRoleMapper taskRoleMapper;

    @Autowired
    private TaskGradeItemMapper taskGradeItemMapper;

    @Autowired
    private TaskWarningPlanMapper taskWarningPlanMapper;

    @Reference(lazy = true)
    private DiaryService diaryService;
    @Reference
    private SsoService ssoService;
    @Reference(lazy = true)
    private WarningService warningService;
    @Autowired
    private JmsTemplate template;

    private ArrayList<Date> days(TaskVO taskVO, Calendar cal, Calendar cal2) {
        //起始时间和结束时间间隔多少天
//            int a = (int) ((taskEndTime.getTime() - taskStartTime.getTime()) / (1000 * 60 * 60 * 24));
        int day1 = cal.get(Calendar.DAY_OF_YEAR);
        int day2 = cal2.get(Calendar.DAY_OF_YEAR);
        int a;
        int year1 = cal.get(Calendar.YEAR);
        int year2 = cal2.get(Calendar.YEAR);
        if (year1 != year2) {
            int timeDistance = 0;
            for (int i = year1; i < year2; i++) {
                if (i % 4 == 0 && i % 100 != 0 || i % 400 == 0) {//闰年,几乎用不到
                    timeDistance += 366;
                } else {
                    timeDistance += 365;
                }
            }
            a = timeDistance + (day2 - day1);
        } else {
            a = day2 - day1;
        }
        ArrayList<Date> dateList = new ArrayList<>();//
        //此时要处理时间(周一周二等),首先获取星期集合
        ArrayList<Integer> weeks = taskVO.getWeeks();
        //如果集合存在1,代表有星期日,7代表周六
        for (int j = 1; j < 8; j++) {
            if (weeks.contains(j)) {
                for (int i = 0; i < a + 1; i++) {
                    cal.add(Calendar.DATE, i);//i天后的日期
                    if ((cal.get(Calendar.DAY_OF_WEEK)) == j) {//
                        dateList.add(cal.getTime());
                    }
                    cal.setTime(taskVO.getTask().getTaskStartTime());
                }

            }
        }
        return dateList;
    }

    @Override
    public void insert1(TaskVO taskVO, String token) {
        ActiveMQTopic taskInsert = new ActiveMQTopic(ACTIVEMQ_TOPIC_TASK_KEY);
        Long id = taskVO.getTask().getId();
        template.convertAndSend(taskInsert, id);
        Object user = ssoService.tokenUser(token);
        if (user instanceof Student) {
            taskVO.getTask().setStatus(3);
        }
        insert(taskVO);
    }

    @Transactional
    @Override
    public void insert(TaskVO taskVO) {
        //判断登录用户是不是学生
        //接收TaskVo的json对象进行拆分,首先拆出task对象
        Date date = new Date();
        Task task = taskVO.getTask();
        task.setCreateTime(date);
        task.setUpdateTime(date);
        //执行插入方法插入task对象到表中
        taskMapper.insert(task);
        //拆分出签到时间规则属性
        Integer dateType = taskVO.getDateType();
        //判断时间类型,执行不同的签到规则,处理时间
        //如果签到时间类型为1,不需处理
        ArrayList<SignInDate> signInDateList = new ArrayList<>();//
        if (dateType == 1) {//
            //把每个时间遍历到signInDateList
            ArrayList<Date> signInDates = taskVO.getSignInDates();
            for (Date sDate : signInDates) {
                SignInDate signInDate = new SignInDate();
                signInDate.setCreateTime(date);
                signInDate.setUpdateTime(date);
                signInDate.setStatus(task.getStatus());
                signInDate.setTaskId(task.getId());
                signInDate.setDate(sDate);
                signInDateList.add(signInDate);
            }
            signInDateService.insert(signInDateList);
        } else {
            Date taskStartTime = task.getTaskStartTime();
            Calendar cal = Calendar.getInstance();
            //date转calendar
            cal.setTime(taskStartTime);
            Date taskEndTime = task.getTaskEndTime();
            Calendar cal2 = Calendar.getInstance();
            cal2.setTime(taskEndTime);
            //调用时间分割方法days
            ArrayList<Date> dateList = days(taskVO, cal, cal2);
            //遍历放入数据
            for (Date date1 : dateList) {
                SignInDate signInDate = new SignInDate();
                signInDate.setCreateTime(date);
                signInDate.setUpdateTime(date);
                signInDate.setStatus(task.getStatus());
                signInDate.setTaskId(task.getId());
                signInDate.setDate(date1);
                signInDateList.add(signInDate);
            }
        }
        signInDateService.insert(signInDateList);
        //拆分出taskRoles
        ArrayList<TaskRole> taskRoles = taskVO.getTaskRoles();//
        for (TaskRole taskRole : taskRoles) {
            taskRole.setCreateTime(date);
            taskRole.setUpdateTime(date);
            taskRole.setStatus(task.getStatus());
            taskRole.setGradingType(task.getGradingType());
            taskRole.setTaskId(task.getId());
        }
        taskRoleService.insert(taskRoles);
        //拆分出taskStudentId集合
        ArrayList<Long> taskStudents1 = taskVO.getTaskStudents();//
        ArrayList<TaskStudent> taskStudents = new ArrayList<>();
        for (Long studentId : taskStudents1) {
            TaskStudent taskStudent = new TaskStudent();
            taskStudent.setUpdateTime(date);
            taskStudent.setCreateTime(date);
            taskStudent.setStatus(task.getStatus());
            taskStudent.setTaskId(task.getId());
            taskStudent.setStudentId(studentId);
            taskStudents.add(taskStudent);
        }
        taskStudentService.insert(taskStudents);
    }

    @Transactional
    @Override
    public void delete(Long id) {
        taskMapper.delete(id);
    }

    @Transactional
    @Override
    public void deletes(Long[] ids) {
        taskMapper.deletes(ids);
        taskRoleService.deletesByTaskIds(ids);
        taskStudentService.deletesByTaskIds(ids);
        signInDateService.deletesByTaskIds(ids);
    }

    @Transactional
    @Override
    public void update(Long id, TaskVO taskVO, String token) {
        Date date = new Date();
        Task task = taskVO.getTask();
        task.setUpdateTime(date);
        //先删除,再添加
        delete(id);
        insert(taskVO);
    }

    @Override
    public TaskVO findTaskVOById(Long id) {
        String s = REDIS_TASK_KEY_PREFIX + id;
        //通过key去redis里查
        String s1 = redisTemplate.opsForValue().get(s);
        //判断，如果空，先把数据从mybatis里查出来，然后存进redis里
        TaskVO taskVO = new TaskVO();
        if (s1 == null) {
            Task task = taskMapper.findById(id);
            taskVO.setTask(task);
            ArrayList<TaskRole> taskRoles = taskRoleService.findByTaskId(id);
            taskVO.setTaskRoles(taskRoles);
            ArrayList<Long> taskStudents = taskStudentService.findByTaskId(id);
            taskVO.setTaskStudents(taskStudents);
            String s2 = JSON.toJSONString(taskVO);
            redisTemplate.opsForValue().set(s, s2, REDIS_TASK_KEY_EXPIRE, TimeUnit.SECONDS);
            return taskVO;
        } else {
            redisTemplate.expire(s, REDIS_TASK_KEY_EXPIRE, TimeUnit.SECONDS);
            return JSON.parseObject(s1, TaskVO.class);

        }


    }

    @Override
    public Task findById(Long id) {
        return taskMapper.findById(id);
    }

    @Override
    public Map<String, List<Diary>> diarySituation(Long taskId) {
        HashMap<String, List<Diary>> result = new LinkedHashMap<>();
        List<SignInDate> signTime = signInDateService.findSignTime(taskId);
        for (SignInDate signInDate : signTime) {
            List<Diary> diarySituation = diaryService.findDiarySituation(signInDate.getDate(), taskId);
            result.put(signInDate.getDate().toString(), diarySituation);
        }
        return result;
    }

    @Override
    public PageInfo<SimpleTask> findDivertTasksByTeachlinkId(Long teachlinkId, Pageable page) {
        PageHelper.startPage(page.getPage(), page.getSize());
        List<SimpleTask> tasks = taskMapper.findTasksByTeachlinkId(teachlinkId);
        return PageInfo.of(tasks);
    }

    @Override
    public void updates(Long[] taskIds, Task task) {
        taskMapper.updates(taskIds, task);
    }

    @Override
    public PageInfo<TaskResult> findStuSubmitTasks(Long teacherId, Pageable page) {
        PageHelper.startPage(page.getPage(), page.getSize());
        List<TaskResult> items = this.taskMapper.findStuSubmitTasks(teacherId);
        return PageInfo.of(items);
    }

    @Override
    public boolean passTask(Long teacherId, Long taskId) {
        Task task = this.taskMapper.findById(taskId);
        // 如果当前登录的老师是负责老师中的任意一个
        // TODO: 2019/8/6 判断老师的权限
//        if (ObjectUtils.nullSafeEquals(teacherId, task.getTeacherId1())
//                || ObjectUtils.nullSafeEquals(teacherId, task.getTeacherId2())) {
        Date now = DateUtils.now();
        // 审核通过 将任务的状态设置为: 未开始
        task.setStatus(0);
        task.setUpdateTime(now);
        this.taskMapper.updateStatus(task);
        return true;
//        }
//        return false;
    }

    @Override
    public boolean denyTask(Long teacherId, Long taskId) {
        Task task = this.taskMapper.findById(taskId);
        // 如果当前登录的老师是负责老师中的任意一个
        // TODO: 2019/8/6 判断老师的权限
//        if (ObjectUtils.nullSafeEquals(teacherId, task.getTeacherId1())
//                || ObjectUtils.nullSafeEquals(teacherId, task.getTeacherId2())) {
        Date now = DateUtils.now();
        // 审核通过 将任务的状态设置为: 未开始
        task.setStatus(4);
        task.setUpdateTime(now);
        this.taskMapper.updateStatus(task);
        return true;
//        }
//        return false;
    }

    @Transactional
    @Override
    public void newTask(Long teacherId, TaskBody taskBody) throws PtpException {

        Date now = DateUtils.now();

        // 1. ----设置任务的基本信息----
        Task task = new Task();
        BeanUtils.copyProperties(taskBody, task);
        task.setCreateTime(now);
        task.setUpdateTime(now);
        task.setStatus(0);
        // 2. 指导老师
        List<Long> teacherIds = taskBody.getTeacherIds();
        if (CollectionUtils.isEmpty(teacherIds) || teacherIds.size() > 2) {
            throw new PtpException(400, "请选择1个或2个指导教师");
        }
        // 设置第一个指导教师
        task.setTeacherId1(teacherIds.get(0));
        // 如果有第二个指导教师, 设置第二个
        if (teacherIds.size() == 2) {
            task.setTeacherId2(teacherIds.get(1));
        }
        // ----任务的基本信息设置完成, 准备插入, 获取到生成的id----
        this.taskMapper.insert(task);
        Long taskId = task.getId();
        log.info("新增任务, 任务插入完成, id: {}", taskId);

        // 3. 获取计算签到日期时间
        List<SignInDate> signInDates = calculateSignInDates(taskBody, taskId);
        // 记录该任务的具体日期
        // -------------- 获取具体的任务日期 end ----------------
        this.signInDateMapper.insertAll(signInDates);

        // 4. 判断报名模式
        if (taskBody.getDistribution() == 0) {
            // 0代表 统一报名
            List<TaskStudent> taskStudents = getTaskStudents(taskBody, taskId);

            this.taskStudentMapper.insertAll(taskStudents);
            // 1代表 自主报名
            // 需要获取报名的起始和结束日期
            // 字段名一样, 上面可以自动赋值, 忽略处理
        }

        // 5. 评分权重信息
        List<TaskRole> taskRoles = getTaskRoles(taskBody, taskId);
        log.info("新增任务, 两条评分数据: {}", taskRoles);
        this.taskRoleMapper.insertAll(taskRoles);

        // 6. 具体评分项的权重
        List<TaskGradeItem> items = getTaskGradeItems(taskBody, taskId);
        log.info("新增任务, 评分权重项: {}", items);
        this.taskGradeItemMapper.addAll(items);

        // 7. 预警方案
        List<TaskWarningPlan> plans = getTaskWarningPlans(taskBody, taskId);
        log.info("新增任务, 预警方案: {}", plans);
        this.warningService.insertTaskWarningPlans(plans);
    }

    private List<TaskWarningPlan> getTaskWarningPlans(TaskBody taskBody, Long taskId) {
        List<TaskWarningPlan> taskWarningPlans = new ArrayList<>();
        Long signinWarningId = taskBody.getSigninWarning();
        Long logWarningId = taskBody.getLogWarning();
        Date now = new Date();
        if (signinWarningId != null) {
            taskWarningPlans.add(new TaskWarningPlan(null, taskId, signinWarningId, WarningPlan.TYPE_SIGN, 0, now,
                    now));
        }
        if (logWarningId != null) {
            taskWarningPlans.add(new TaskWarningPlan(null, taskId, logWarningId, WarningPlan.TYPE_DIARY, 0, now,
                    now));
        }
        return taskWarningPlans;
    }

    @Transactional
    @Override
    public void updateTask(TaskBody taskBody) throws PtpException {
        // 1. 设置任务的基本信息
        if (taskBody.getTeachlinkId() == null) {
            throw new PtpException(400, "需要选择教学环节");
        }
        Date now = DateUtils.now();
        Task task = this.taskMapper.findOneByTeachlinkId(taskBody.getTeachlinkId());
        log.info("更新任务 -> 原始任务数据: {}", task);
        // 1. 设置任务的基本信息
        BeanUtils.copyProperties(taskBody, task);
        // 更新时间
        task.setUpdateTime(now);
        log.info("更新任务 -> 修改后任务数据: {}", task);
        // 2. 指导老师
        List<Long> teacherIds = taskBody.getTeacherIds();
        if (CollectionUtils.isEmpty(teacherIds) || teacherIds.size() > 2) {
            throw new PtpException(400, "请选择1个或2个指导教师");
        }
        // 设置第一个指导教师
        task.setTeacherId1(teacherIds.get(0));
        // 如果有第二个指导教师, 设置第二个
        if (teacherIds.size() == 2) {
            task.setTeacherId2(teacherIds.get(1));
        }
        // ----任务的基本信息设置完成, 更新任务----
        this.taskMapper.update(task);

        Long taskId = task.getId();

        // 3. 计算签到日期
        // 获取计算签到日期时间
        List<SignInDate> signInDates = calculateSignInDates(taskBody, taskId);
        // 删除原来的签到时间, 重新插入
        this.signInDateMapper.deleteByTaskId(taskId);
        // 记录该任务的具体日期
        this.signInDateMapper.insertAll(signInDates);

        // 4. 插入统一报名所选择的学生信息
        // 判断报名模式
        if (taskBody.getDistribution() == 0) {
            // 0代表 统一报名
            List<TaskStudent> taskStudents = getTaskStudents(taskBody, taskId);
            // 删除原来的学生信息
            this.taskStudentMapper.deleteByTaskId(taskId);
            this.taskStudentMapper.insertAll(taskStudents);
            // 1代表 自主报名
            // 需要获取报名的起始和结束日期
            // 字段名一样, 上面可以自动赋值, 忽略处理
        }

        // 5. 评分权重信息
        List<TaskRole> taskRoles = getTaskRoles(taskBody, taskId);
        log.info("修改任务, 两条评分数据: {}", taskRoles);
        // 先删除后新增
        this.taskRoleMapper.deleteByTaskId(taskId);
        this.taskRoleMapper.insertAll(taskRoles);

        // 6. 具体评分项的权重
        List<TaskGradeItem> items = getTaskGradeItems(taskBody, taskId);
        log.info("修改任务, 评分权重项: {}", items);
        // 先删除后新增
        this.taskGradeItemMapper.deleteByTaskId(taskId);
        this.taskGradeItemMapper.addAll(items);

        // 7. 预警方案
        List<TaskWarningPlan> plans = getTaskWarningPlans(taskBody, taskId);
        log.info("修改任务, 预警方案: {}", plans);
        // 先删除后新增
        this.warningService.deleteTaskWarningPlansByTaskId(taskId);
        this.warningService.insertTaskWarningPlans(plans);
    }

    /**
     * 具体评分项的权重
     *
     * @param taskBody
     * @param taskId
     * @return
     */
    private List<TaskGradeItem> getTaskGradeItems(TaskBody taskBody, Long taskId) {
        Date now = DateUtils.now();
        List<Double> ratios = new ArrayList<>();
        ratios.add(taskBody.getCheckScore());
        ratios.add(taskBody.getLogScore());
        ratios.add(taskBody.getReportScore());
        ratios.add(taskBody.getOrdinaryScore1());
        ratios.add(taskBody.getOrdinaryScore2());
        ratios.add(taskBody.getOtherScore());

        double sum = ratios.stream().mapToDouble(item -> item).sum();
        if (sum != 100.0) {
            throw new PtpException(400, "所有评分项的和必须为100");
        }

        List<TaskGradeItem> items = new ArrayList<>();
        for (int i = 0; i < ratios.size(); i++) {
            TaskGradeItem item = TaskGradeItem.builder()
                    .taskId(taskId)
                    .gradeItemId(i + 1L)
                    .ratio(ratios.get(i) / 100)
                    .createTime(now)
                    .updateTime(now)
                    .build();
            items.add(item);
        }
        return items;
    }

    @Override
    public List<TaskGradeItem> getTaskGradeItemsByTaskId(Long taskId) {
        return taskGradeItemMapper.findByTaskId(taskId);
    }

    /**
     * 评分权重信息
     *
     * @param taskBody
     * @param taskId
     * @return
     */
    private List<TaskRole> getTaskRoles(TaskBody taskBody, Long taskId) {
        Date now = DateUtils.now();
        // 指导老师的评分权重
        double teacherRatio = taskBody.getTeacherMaturityScore();
        // 实习单位的评分权重
        double baseRatio = taskBody.getBaseMaturityScore();
        // 判断两个值之和是不是等于100
        if ((teacherRatio + baseRatio) != 100.0) {
            throw new PtpException(400, "两个评分权重值的和必须等于100");
        }

        TaskRole teacherTaskRole = TaskRole.builder()
                .roleId(Role.ROLE_SCHOOL_TEACHER) // TODO: 2019/8/5 指导老师的角色ID
                .taskId(taskId)
                .gradingType(taskBody.getGradingType())
                .ratio(teacherRatio / 100)
                .createTime(now)
                .updateTime(now)
                .status(0).build();
        TaskRole baseTaskRole = TaskRole.builder()
                .roleId(Role.ROLE_BASE_TEACHER) // TODO: 2019/8/5 基地单位的角色ID
                .taskId(taskId)
                .gradingType(taskBody.getGradingType())
                .ratio(baseRatio / 100)
                .createTime(now)
                .updateTime(now)
                .status(0).build();
        return Arrays.asList(teacherTaskRole, baseTaskRole);
    }

    /**
     * 获取统一报名时选择的所有学生信息
     *
     * @param taskBody
     * @param taskId
     * @return
     */
    private List<TaskStudent> getTaskStudents(TaskBody taskBody, Long taskId) {
        Date now = DateUtils.now();
        // 需要获取具体学生的id
        List<Long> studentIds = taskBody.getStudentIds();
        if (CollectionUtils.isEmpty(studentIds)) {
            throw new PtpException(400, "请选择报名的学生");
        }
        log.info("新增 or 修改任务, 获取的学生id: {}", studentIds);
        // 统一报名, 记录所有需要参加该任务的学生
        List<TaskStudent> taskStudents = studentIds.stream()
                .map(studentId -> new TaskStudent(taskId, studentId, 0, now, now))
                .collect(Collectors.toList());
        log.info("新增 or 修改任务, 插入任务与学生中间表: {}", taskStudents);
        return taskStudents;
    }

    private List<SignInDate> calculateSignInDates(TaskBody taskBody, Long taskId) {
        Date now = DateUtils.now();
        // 获取任务开始时间和结束时间
        Date taskStartTime = taskBody.getTaskStartTime();
        Date taskEndTime = taskBody.getTaskEndTime();
        if (taskEndTime.before(taskStartTime)) {
            throw new PtpException(400, "开始时间不能晚于结束时间");
        }
        Date today = DateUtils.getTodayDateIgnoreTime();
        if (taskStartTime.before(today)) {
            throw new PtpException(400, "开始时间不能早于今天");
        }
        // -------------- 获取具体的任务日期 start ----------------
        // 判断日期模式选择
        TaskDateHandler dateHandler;
        // 根据不同的日期模式选择不同的日期处理器
        switch (taskBody.getDateTime()) {
            case 0:// 工作日
                dateHandler = new WorkdayDateHandler(taskStartTime, taskEndTime);
                break;
            case 1:// 每周几
                List<Integer> timeCheckList = taskBody.getTimeCheckList();
                dateHandler = new DayOfWeekDateHandler(taskStartTime, taskEndTime, timeCheckList);
                break;
            case 2:// 具体日期
                List<Date> days = taskBody.getTtimes();
                dateHandler = new DefaultDateHandler(days);
                break;
            default:
                throw new PtpException(400, "需要选择日期模式");
        }
        // 获取到具体日期
        List<Date> workDayList = dateHandler.dispose();
        log.info("新增任务, 具体日期为: {}", workDayList);
        // 将日期信息存到sign_in_date表
        List<SignInDate> signInDates = workDayList.stream()
                .map(date -> new SignInDate(date, taskId, 0, now, now))
                .collect(Collectors.toList());
        log.info("新增任务, 插入签到日期表: {}", signInDates);
        return signInDates;
    }

    @Override
    public List<Task> findByStudentId(Long studentId) {
        return taskMapper.findByStudentId(studentId);
    }

    @Override
    public Task findTaskDescById(Long taskId) {
        return taskMapper.findTaskDescById(taskId);
    }

    @Override
    public CentralTaskResult findCentralTaskByTeachlinkId(Long teachlinkId) {

        // 0. 查询教学环节下的任务详情
        CentralTaskResult r = this.taskMapper.findCentralTaskByTeachlinkId(teachlinkId);
        Long taskId = r.getTaskId();
        // 1. 实习进度(只是所有任务日期及今天的日期)
        Date today = DateUtils.now();
        List<Date> dateDetails = this.signInDateMapper.findDetailsByTaskId(taskId);
        long count = dateDetails.stream().filter(date -> date.before(today)).count();
        Double rate = count * 100.0 / dateDetails.size();
        BigDecimal bd = BigDecimal.valueOf(rate);
        double value = bd.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
        CentralTaskResult.Schedule schedule = new CentralTaskResult.Schedule(value, today, dateDetails);
        r.setSchedule(schedule);
        // 2. 查询该任务下所有学生的任务完成度等信息
        // 3. 查询所有学生的今日考勤信息
        // 4. 查询所有学生的总出勤率
        // 截至到当天为止某个任务应该打卡的总次数
        Integer total = this.signInDateMapper.checkTimes(taskId, today);
        List<CentralTaskResult.Student> students = this.signInDateMapper.findStudentDetails(taskId, today, total);
        r.setStudents(students);
        // 5. 查询所有学生从任务开始时直到今天的日志完成情况
        // 单独的一个接口完成的 DiaryController

        return r;
    }

    @Override
    public Task findTaskByTeachlinkId(Long teachlinkId) {
        return this.taskMapper.findOneByTeachlinkId(teachlinkId);
    }

    @Override
    public List<Task> findByStatus(Integer status) {
        return taskMapper.findByStatus(status);
    }

    @Override
    public boolean updateStatusById(Integer status, Long taskId) {
        Task task = new Task();
        task.setId(taskId);
        task.setUpdateTime(new Date());
        task.setStatus(status);
        return taskMapper.updateStatus(task) > 0;
    }

    @Override
    public void startTask(Long taskId) {
        updateStatusById(1, taskId);
    }

    @Override
    public void endTask(Long taskId) {
        updateStatusById(2, taskId);
    }

    @Override
    public TaskBody findTaskBodyByTeachlink(Long teachlinkId) {
        TaskBody body = new TaskBody();
        Task task = this.taskMapper.findOneByTeachlinkId(teachlinkId);
        BeanUtils.copyProperties(task, body);

        // 1. 整理指导教师数据
        List<Long> teacherIds = Arrays.asList(task.getTeacherId1(), task.getTeacherId2());

        // 2. 日期模式选择 ???
        // TODO: 2019/9/3 日期模式

        // 3. 具体日期
        List<Date> ttimes = this.signInDateMapper.findDetailsByTaskId(task.getId());

        // 4. 统一报名模式的所有学生id
        List<Long> studentIds = this.taskStudentMapper.findByTaskId(task.getId());

        // 5. 指导老师和实习单位的评分权重
        List<TaskRole> taskRoles = this.taskRoleMapper.findByTaskId(task.getId());
        // 指导老师评分权重
        Double teacherMaturityScore = taskRoles.stream()
                .filter(item -> item.getRoleId() == 1L)
                .mapToDouble(TaskRole::getRatio)
                .findFirst()
                .orElse(0);
        // 实习单位评分权重
        Double baseMaturityScore = taskRoles.stream()
                .filter(item -> item.getRoleId() == 3L)
                .mapToDouble(TaskRole::getRatio)
                .findFirst()
                .orElse(0);

        // 6. 评分
        List<TaskGradeItem> items = this.taskGradeItemMapper.findByTaskId(task.getId());
        Double checkScore = getRatio(items, 0);
        Double logScore = getRatio(items, 1);
        Double reportScore = getRatio(items, 2);
        Double ordinaryScore1 = getRatio(items, 3);
        Double ordinaryScore2 = getRatio(items, 4);
        Double otherScore = getRatio(items, 5);

        List<TaskWarningPlan> taskWarningPlans = taskWarningPlanMapper.findByTaskId(task.getId());

        // 为body赋值
        body.setTeacherIds(teacherIds);
        body.setTtimes(ttimes);
        body.setStudentIds(studentIds);
        body.setTeacherMaturityScore(teacherMaturityScore * 100);
        body.setBaseMaturityScore(baseMaturityScore * 100);
        body.setCheckScore(checkScore * 100);
        body.setLogScore(logScore * 100);
        body.setReportScore(reportScore * 100);
        body.setOrdinaryScore1(ordinaryScore1 * 100);
        body.setOrdinaryScore2(ordinaryScore2 * 100);
        body.setOtherScore(otherScore * 100);

        body.setSigninWarning(taskWarningPlans.get(0).getWarningPlanId());
        body.setLogWarning(taskWarningPlans.get(1).getWarningPlanId());

        return body;
    }

    @Override
    public List<Long> findTaskIdsByTeacher(Teacher teacher) {
        if (teacher.getMajorId() != null) {
            return taskMapper.findTaskIdByMajorTeacher(teacher.getId());
        } else {
            return taskMapper.findTaskIdByCollegeTeacher(teacher.getCollegeId());
        }
    }

    @Override
    public List<Task> showByStudentId(Long id) {
        return taskMapper.showByStudentId(id);
    }

    @Override
    public List<Task> findByBaseId(Long baseId) {
        return taskMapper.findByBaseId(baseId);
    }

    @Override
    public void updateEvaluate(Long taskId, int evaluate) {
        Task task = new Task();
        task.setId(taskId);
        task.setEvaluate(evaluate);
        task.setUpdateTime(new Date());
        taskMapper.update(task);
    }

    private Double parseDouble(String value) {
        try {
            return Double.parseDouble(value);
        } catch (Exception e) {
            return null;
        }
    }

    private Double getRatio(List<TaskGradeItem> items, int i) {
        return items.size() > i ? items.get(i).getRatio() : 0;
    }


}
